#pragma once
// -------------------------------------------------
// ZED Kit
// -------------------------------------------------
//   File Name: mask.hpp
//      Author: Ziming Li
//     Created: 2023-03-08
// -------------------------------------------------
// Copyright (C) 2023 MingYang Software Technology.
// -------------------------------------------------

#ifndef ZED_TYPES_MASK_HPP
#define ZED_TYPES_MASK_HPP

#include <type_traits>

namespace zed {

namespace detail {
template <unsigned N>
struct mask_trait {};
}

template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
class mask
{
    using mask_type = typename detail::mask_trait<sizeof(E)>::mask_type;
public:
    constexpr mask(void) = default;
    constexpr mask(E val) : mask(static_cast<mask_type>(val)) {}

    constexpr bool operator==(E v) const;
    constexpr mask& operator|=(E v);
    constexpr mask operator&(E v) const;
private:
    constexpr mask(mask_type val) : m_val(val) {}

    mask_type m_val = 0;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace detail {

template <>
struct mask_trait<1> { using mask_type = uint8_t; };

template <>
struct mask_trait<2> { using mask_type = uint16_t; };

template <>
struct mask_trait<4> { using mask_type = uint32_t; };

template <>
struct mask_trait<8> { using mask_type = uint64_t; };

}

template <typename E, typename _>
constexpr bool mask<E, _>::operator==(E v) const
{
    return m_val == static_cast<mask_type>(v);
}

template <typename E, typename _>
constexpr mask<E, _>& mask<E, _>::operator|=(E v)
{
    m_val |= static_cast<mask_type>(v);
    return *this;
}

template <typename E, typename _>
constexpr mask<E, _> mask<E, _>::operator&(E v) const
{
    mask_type val = m_val & static_cast<mask_type>(v);
    return mask(val);
}

} // namespace zed

#endif // ZED_TYPES_MASK_HPP
